package cn.tom.tompicturebackend.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.tom.tompicturebackend.exception.BusinessException;
import cn.tom.tompicturebackend.exception.ErrorCode;
import cn.tom.tompicturebackend.exception.ThrowUtils;
import cn.tom.tompicturebackend.manager.FileManager;
import cn.tom.tompicturebackend.model.dto.file.UploadPictureResult;
import cn.tom.tompicturebackend.model.dto.picture.*;
import cn.tom.tompicturebackend.model.entity.Space;
import cn.tom.tompicturebackend.model.entity.User;
import cn.tom.tompicturebackend.model.enums.PictureReviewStatusEnum;
import cn.tom.tompicturebackend.model.vo.PictureVO;
import cn.tom.tompicturebackend.model.vo.UserVo;
import cn.tom.tompicturebackend.service.SpaceService;
import cn.tom.tompicturebackend.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.tom.tompicturebackend.model.entity.Picture;
import cn.tom.tompicturebackend.service.PictureService;
import cn.tom.tompicturebackend.mapper.PictureMapper;
import com.fasterxml.jackson.core.io.IOContext;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
* @author 31149
* @description 针对表【picture(图片)】的数据库操作Service实现
* @createDate 2025-02-07 15:11:03
*/
@Service
@Slf4j
public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture> implements PictureService
{

    @Resource
    private FileManager fileManager;

    @Resource
    private UserService userService;

    @Resource
    private SpaceService spaceService;


    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private ThreadPoolExecutor customExecutor;



    @Override
    public PictureVO uploadPicture(MultipartFile multipartFile, PictureUploadRequest pictureUploadRequest, User loginUser)
    {
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NOT_LOGIN_ERROR);
        // 用于判断是更新还是新增
        Long pictureId = null;
        if(pictureUploadRequest != null){
            pictureId = pictureUploadRequest.getId();
        }
        Long spaceId = pictureUploadRequest.getSpaceId();
        if(spaceId !=null){
            Space space = spaceService.getById(spaceId);
            ThrowUtils.throwIf(space == null, ErrorCode.NOT_LOGIN_ERROR,"空间不存在");
            // 必须空间创建人（管理员）才能上传
            if (!loginUser.getId().equals(space.getUserId())) {
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "没有空间权限");
            }
            // 校验额度
            if (space.getTotalCount() >= space.getMaxCount()) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "空间条数不足");
            }
            if (space.getTotalSize() >= space.getMaxSize()) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "空间大小不足");
            }
        }


        // 如果是更新图片，需要检验图片是否存在
        if(pictureId !=null){
//            boolean exists = this.lambdaQuery().eq(Picture::getId,pictureId).exists();
//            ThrowUtils.throwIf(!exists,ErrorCode.NOT_LOGIN_ERROR,"图片不存在");
            Picture oldPicture = this.getById(pictureId);

            ThrowUtils.throwIf(oldPicture ==null,ErrorCode.NOT_FOUND_ERROR,"图片不存在");

            //仅本人或管理员可编辑
            if(oldPicture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)){
                throw new BusinessException(ErrorCode.NOT_AUTHORIZED_ERROR);
            }
            if(spaceId == null){
               if(oldPicture.getSpaceId() != null){
                   spaceId = oldPicture.getSpaceId();
               }
            }else{
                //传了 spaceId，则必须和旧图片一致
                if(ObjUtil.notEqual(spaceId,oldPicture.getSpaceId())){
                    throw new BusinessException(ErrorCode.NOT_AUTHORIZED_ERROR);
                }
            }
        }
        //上传图片，得到信息
        //按照用户id 划分目录
        String uploadPathPrefix ;
        if(spaceId ==null){
            uploadPathPrefix = String.format("public/%s",loginUser.getId());
        }else{
            uploadPathPrefix = String.format("space/%s",spaceId);
        }
        UploadPictureResult uploadPictureResult = fileManager.uploadPictureResult(multipartFile,uploadPathPrefix);
        //构造要入库的图片信息
        Picture picture = new Picture();
        picture.setSpaceId(spaceId);
        picture.setUrl(uploadPictureResult.getUrl());
        picture.setName(uploadPictureResult.getPicName());
        picture.setPicSize(uploadPictureResult.getPicSize());
        picture.setPicWidth(uploadPictureResult.getPicWidth());
        picture.setPicHeight(uploadPictureResult.getPicHeight());
        picture.setPicScale(uploadPictureResult.getPicScale());
        picture.setPicFormat(uploadPictureResult.getPicFormat());
        picture.setUserId(loginUser.getId());

        //补充审核参数
        fillReviewParams(picture,loginUser);

        //如果pictureId 不为空，表示更新，否则是新增
        if(pictureId !=null){
            picture.setId(pictureId);
            picture.setEditTime(new Date());
        }
        // 开启事务
        Long finalSpaceId = spaceId;
        transactionTemplate.execute(status -> {
            boolean result = this.saveOrUpdate(picture);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "图片上传失败");
            if (finalSpaceId != null) {
                boolean update = spaceService.lambdaUpdate()
                        .eq(Space::getId, finalSpaceId)
                        .setSql("totalSize = totalSize + " + picture.getPicSize())
                        .setSql("totalCount = totalCount + 1")
                        .update();
                ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "额度更新失败");
            }
            return picture;
        });




        return PictureVO.objToVo(picture);
    }

    @Override
    public QueryWrapper<Picture> getQueryWrapper(PictureQueryRequest queryRequest) {

        QueryWrapper<Picture> queryWrapper = new QueryWrapper<>();
        if(queryRequest != null){
            return queryWrapper;
        }
        // 从对象中取值
        Long id = queryRequest.getId();
        String name = queryRequest.getName();
        String introduction = queryRequest.getIntroduction();
        String category = queryRequest.getCategory();
        List<String> tags = queryRequest.getTags();
        Long picSize = queryRequest.getPicSize();
        Integer picWidth = queryRequest.getPicWidth();
        Integer picHeight = queryRequest.getPicHeight();
        Double picScale = queryRequest.getPicScale();
        String picFormat = queryRequest.getPicFormat();
        Long userId = queryRequest.getUserId();
        int current = queryRequest.getCurrent();
        int pageSize = queryRequest.getPageSize();
        String sortField = queryRequest.getSortField();
        String sortOrder = queryRequest.getSortOrder();
        String searchText = queryRequest.getSearchText();
        Integer reviewStatus = queryRequest.getReviewStatus();
        String reviewMessage = queryRequest.getReviewMessage();
        Long reviewId = queryRequest.getReviewerId();
        Long spaceId = queryRequest.getSpaceId();
        Boolean nullSpaceId = queryRequest.isNullSpaceId();



        // 拼接查询条件
        if(StrUtil.isNotBlank(searchText)){
            //需要拼接查询条件
            queryWrapper.and(qw -> qw.like("name", searchText).or().like("introduction", searchText));
        }
        queryWrapper.eq(ObjUtil.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjUtil.isNotEmpty(userId), "user_id", userId);
        queryWrapper.like(StrUtil.isNotBlank(name), "name", name);
        queryWrapper.like(StrUtil.isNotBlank(introduction), "introduction", introduction);
        queryWrapper.eq(StrUtil.isNotBlank(category), "category", category);
        queryWrapper.eq(ObjUtil.isNotEmpty(picWidth), "pic_width", picWidth);
        queryWrapper.eq(ObjUtil.isNotEmpty(picHeight), "pic_height", picHeight);
        queryWrapper.eq(ObjUtil.isNotEmpty(picSize), "pic_size", picSize);
        queryWrapper.eq(ObjUtil.isNotEmpty(picScale), "pic_scale", picScale);
        queryWrapper.like(StrUtil.isNotBlank(picFormat), "pic_format", picFormat);

        queryWrapper.eq(ObjUtil.isNotEmpty(reviewStatus),"reviewStatus",reviewStatus);
        queryWrapper.like(StrUtil.isNotBlank(reviewMessage),"reviewMessage",reviewMessage);
        queryWrapper.eq(ObjUtil.isNotEmpty(reviewId),"reviewerId",reviewId);
        queryWrapper.eq(ObjUtil.isNotEmpty(spaceId), "spaceId", spaceId);
        queryWrapper.isNull(nullSpaceId, "spaceId");

        Date startEditTime = queryRequest.getStartEditTime();
        Date endEditTime = queryRequest.getEndEditTime();
        queryWrapper.ge(ObjUtil.isNotEmpty(startEditTime), "editTime", startEditTime);
        queryWrapper.lt(ObjUtil.isNotEmpty(endEditTime), "editTime", endEditTime);




        //JSON 数组查询
        if(CollUtil.isNotEmpty(tags)){
            for (String tag : tags) {
                queryWrapper.like("tags", "\""+ tag + "\"");
            }
        }
        //排序
        queryWrapper.orderBy(StrUtil.isNotEmpty(sortField),sortOrder.equals("ascend"), sortField);


        return queryWrapper;
    }

    @Override
    public PictureVO getPictureVO(Picture picture, HttpServletRequest request) {
        //对象转换
        PictureVO pictureVO = PictureVO.objToVo(picture);

        //获取用户信息
        Long userId = picture.getUserId();
        if(userId != null && userId > 0){
            User user = userService.getById(userId);
            UserVo userVo = userService.getUserVo(user);
            pictureVO.setUser(userVo);
        }
        return pictureVO;
    }

    @Override
    public Page<PictureVO> getPictureVOPage(Page<Picture> picturePage, HttpServletRequest request) {
        List<Picture> pictureList = picturePage.getRecords();
        Page<PictureVO> pictureVOPage = new Page<>(picturePage.getCurrent(), picturePage.getSize(), picturePage.getTotal());
        if(CollUtil.isEmpty(pictureList)){
            return pictureVOPage;
        }
        //对象列表 =》 封装对象列表
        List<PictureVO> pictureVOList = pictureList.stream().map(PictureVO::objToVo).collect(Collectors.toList());
        //1、关联查询用户信息
        Set<Long> userIdSet = pictureList.stream().map(Picture::getUserId).collect(Collectors.toSet());
        Map<Long,List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream().collect(Collectors.groupingBy(User::getId));
        //2、填充信息
        pictureVOList.forEach(pictureVO -> {
            Long userId = pictureVO.getUserId();
            User user  = null;
            if(userIdUserListMap.containsKey(userId)){
                user = userIdUserListMap.get(userId).get(0);
            }
            pictureVO.setUser(userService.getUserVo(user));
        });
        pictureVOPage.setRecords(pictureVOList);

        return pictureVOPage;
    }

    @Override
    public void validPicture(Picture picture) {
        ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);
        //从对象取值
        Long id = picture.getId();
        String url = picture.getUrl();
        String introduction = picture.getIntroduction();

        //修改数据时,id不能为空，有参数则校验
        ThrowUtils.throwIf(ObjUtil.isNull(id),ErrorCode.PARAMS_ERROR, "id不能为空");
        if (StrUtil.isNotBlank(url)) {
            ThrowUtils.throwIf(url.length() >1024, ErrorCode.PARAMS_ERROR, "url过长");
        }
        if(StrUtil.isNotBlank(introduction)){
            ThrowUtils.throwIf(introduction.length() > 800, ErrorCode.PARAMS_ERROR, "简介过长");
        }
    }

    @Override
    public void doPictureReview(PictureReviewRequest pictureReviewRequest, User longinUser) {
        Long id = pictureReviewRequest.getId();
        Integer reviewStatus = pictureReviewRequest.getReviewStatus();
        PictureReviewStatusEnum reviewStatusEnum = PictureReviewStatusEnum.getEnumByValue(reviewStatus);
        if(id == null || reviewStatusEnum == null || PictureReviewStatusEnum.REVIEWING.equals(reviewStatusEnum)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //判断是否存在
        Picture oldPicture = this.getById(id);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");

        //已是该状态
        if(oldPicture.getReviewStatus().equals(reviewStatus)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请勿重复审核");
        }
        //更新审核状态
        Picture updatePicture = new Picture();
        BeanUtils.copyProperties(pictureReviewRequest, updatePicture);
        updatePicture.setReviewId(longinUser.getId());
        updatePicture.setReviewTime(new Date());
        boolean result = this.updateById(updatePicture);

        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "审核失败");

    }

    @Override
    public void fillReviewParams(Picture picture, User loginUser) {
        if(userService.isAdmin(loginUser)){
            //管理员自动过审
            picture.setReviewStatus(PictureReviewStatusEnum.PASS.getValue());
            picture.setReviewId(loginUser.getId());
            picture.setReviewTime(new Date());
            picture.setReviewMessage("管理员自动过审");
        }else{
            //非管理员，创建或编辑都要改为待审核
            picture.setReviewStatus(PictureReviewStatusEnum.REVIEWING.getValue());
        }
    }

    @Override
    public Integer uploadPictureByBatch(PictureUploadByBatchRequest pictureUploadByBatchRequest, User loginUser) {
        String searchText = pictureUploadByBatchRequest.getSearchText();
        Integer count = pictureUploadByBatchRequest.getCount();

        ThrowUtils.throwIf(count > 30,ErrorCode.PARAMS_ERROR);

        String fetchUrl = String.format("https://cn.bing.com/images/async?q=%s&mmasync=1", searchText);
        Document document;
        try{
            document = Jsoup.connect(fetchUrl).get();
        }catch (IOException e){
            log.error("获取数据失败",e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR);
        }

        Elements imgElementList = document.select("img.mimg");
        int uploadCount = 0;

        for(Element imgElement : imgElementList) {
            String fileUrl = imgElement.attr("src");
            if (StrUtil.isBlank(fileUrl)) {
                log.info("图片地址为空{}",fileUrl);
                continue;
            }
            //处理图片上传地址，防止出现转义问题
            int questionMarkIndex = fileUrl.indexOf("?");
            if(questionMarkIndex > -1) {
                fileUrl = fileUrl.substring(0,questionMarkIndex);
            }
            //上传图片
            PictureUploadRequest pictureUploadRequest = new PictureUploadRequest();
            try{
                //todo
                //PictureVO pictureVO = this.uploadPicture(fileUrl,pictureUploadRequest,loginUser);
                uploadCount++;

            }catch (Exception e){
                log.error("上传图片失败",e);
                continue;
            }
            if (uploadCount >= count) {
                break;
            }
        }
        return uploadCount;

    }

    @Override
    public void checkPictureAuth(User loginUser,Picture picture)
    {
        Long spaceId = picture.getSpaceId();
        if(spaceId ==null){
            if(!picture.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)){
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        }else{
            if(!picture.getUserId().equals(loginUser.getId())){
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
        }
    }

    @Override
    public void deletePicture(long pictureId, User loginUser)
    {
        ThrowUtils.throwIf(pictureId <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser == null, ErrorCode.NO_AUTH_ERROR);
        // 判断是否存在
        Picture oldPicture = this.getById(pictureId);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        // 校验权限
        checkPictureAuth(loginUser, oldPicture);

        // 开启事务
        transactionTemplate.execute(status -> {
            // 操作数据库
            boolean result = this.removeById(pictureId);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
            // 释放额度
            Long spaceId = oldPicture.getSpaceId();
            if (spaceId != null) {
                boolean update = spaceService.lambdaUpdate()
                        .eq(Space::getId, spaceId)
                        .setSql("totalSize = totalSize - " + oldPicture.getPicSize())
                        .setSql("totalCount = totalCount - 1")
                        .update();
                ThrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR, "额度更新失败");
            }
            return true;
        });
// 异步清理文件
        this.clearPictureFile(oldPicture);

        // 异步清理文件
        this.clearPictureFile(oldPicture);
    }

    @Override
    public void editPicture(PictureEditRequest pictureEditRequest, User loginUser) {
        // 在此处将实体类和 DTO 进行转换
        Picture picture = new Picture();
        BeanUtils.copyProperties(pictureEditRequest, picture);
        // 注意将 list 转为 string
        picture.setTags(JSONUtil.toJsonStr(pictureEditRequest.getTags()));
        // 设置编辑时间
        picture.setEditTime(new Date());
        // 数据校验
        this.validPicture(picture);
        // 判断是否存在
        long id = pictureEditRequest.getId();
        Picture oldPicture = this.getById(id);
        ThrowUtils.throwIf(oldPicture == null, ErrorCode.NOT_FOUND_ERROR);
        // 校验权限
        checkPictureAuth(loginUser, oldPicture);
        // 补充审核参数
        this.fillReviewParams(picture, loginUser);
        // 操作数据库
        boolean result = this.updateById(picture);
        ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editPictureByBatch(PictureEditByBatchRequest pictureEditByBatchRequest, User loginUser) {
        List<Long> pictureIdList = pictureEditByBatchRequest.getPictureIdList();
        Long spaceId = pictureEditByBatchRequest.getSpaceId();
        String category = pictureEditByBatchRequest.getCategory();
        List<String> tags = pictureEditByBatchRequest.getTags();

        // 校验权限
        ThrowUtils.throwIf(CollUtil.isEmpty(pictureIdList) ||spaceId ==null, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser ==null,ErrorCode.NOT_LOGIN_ERROR);

        //空间权限
        Space space = spaceService.getById(spaceId);
        ThrowUtils.throwIf(space ==null,ErrorCode.NOT_FOUND_ERROR);
        if(!loginUser.getId().equals(space.getUserId())){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"没有权限修改该空间下的图片");
        }
        //查询指定图片，仅仅选择需要的字段
        List<Picture> pictureList = this.lambdaQuery()
                .select(Picture::getId,Picture::getSpaceId)
                .eq(Picture::getSpaceId,spaceId)
                .in(Picture::getId,pictureIdList)
                .list();
        if(pictureList.isEmpty()){
            return;
        }
        //更新分类和标签
        pictureList.forEach(picture -> {
            if(StrUtil.isNotBlank(category)){
                picture.setCategory(category);
            }
            if(CollUtil.isNotEmpty(tags)){
                picture.setTags(JSONUtil.toJsonStr(tags));
            }
        });
        //更新数据
        boolean result = this.updateBatchById(pictureList);
        ThrowUtils.throwIf(!result,ErrorCode.SYSTEM_ERROR,"更新图片失败");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bathEditPictureMetadata(PictureEditByBatchRequest pictureEditByBatchRequest, Long spaceId, User loginUser) {
       //参数检验
       validateBatchEditRequest(pictureEditByBatchRequest,spaceId,loginUser);
        List<Picture> pictureList = this.lambdaQuery()

                .eq(Picture::getSpaceId,spaceId)
                .in(Picture::getId,pictureEditByBatchRequest.getPictureIdList())
                .list();
        if(pictureList.isEmpty()){
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR);
        }
        //分批处理避免长事务
        int batchSize = 100;
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        for(int i=0;i<pictureList.size();i+=batchSize){

            List<Picture> batch = pictureList.subList(i,Math.min(i+batchSize,pictureList.size()));
            //异步处理每批数据
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                batch.forEach(picture -> {
                    //更新图片信息
                    if(pictureEditByBatchRequest.getCategory() !=null){
                        picture.setCategory(pictureEditByBatchRequest.getCategory());
                    }
                    if(pictureEditByBatchRequest.getTags() !=null){
                        picture.setTags(String.join(",",pictureEditByBatchRequest.getTags()));

                    }
                });
                boolean result = this.updateBatchById(batch);
                if(!result){
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR,"批量更新失败");
                }
            },customExecutor);
            futures.add(future);
        }
        //等待所有任务完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

    }

    void validateBatchEditRequest(PictureEditByBatchRequest pictureEditByBatchRequest, Long spaceId, User loginUser){
        List<Long> pictureIdList = pictureEditByBatchRequest.getPictureIdList();
        String category = pictureEditByBatchRequest.getCategory();
        List<String> tags = pictureEditByBatchRequest.getTags();

        // 校验权限
        ThrowUtils.throwIf(CollUtil.isEmpty(pictureIdList) ||spaceId ==null, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(loginUser ==null,ErrorCode.NOT_LOGIN_ERROR);

        //空间权限
        Space space = spaceService.getById(spaceId);
        ThrowUtils.throwIf(space ==null,ErrorCode.NOT_FOUND_ERROR);
        if(!loginUser.getId().equals(space.getUserId())){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"没有权限修改该空间下的图片");
        }
    }
    public void clearPictureFile(Picture oldPicture) {

    }

}




